local name, ns = ...
WhoRoll = LibStub("AceAddon-3.0"):NewAddon(name, "AceEvent-3.0", "AceTimer-3.0")
local WhoRoll = WhoRoll

local deformat = LibStub("LibDeformat-3.0")

local debug = ns.debug
WhoRoll.debug = debug

local rollHistory = {}
WhoRoll.rollHistory = rollHistory

local MAX_LOOT_DURATION = 180
local timers = {}
function WhoRoll:ScheduleExpirationTimer(itemLink)
	timers[itemLink] = self:ScheduleTimer("ForceFinish", MAX_LOOT_DURATION, itemLink)
end

function WhoRoll:CancelExpirationTimer(itemLink)
	self:CancelTimer(timers[itemLink], true)
end

local currentLoots = {}
WhoRoll.currentLoots = currentLoots

-- globals
local _G = _G
local GREED = GREED
local NEED = NEED
local PASS = PASS
local DISENCHANT = "DISENCHANT"
local YOU = _G.YOU
local GetItemInfo = _G.GetItemInfo
local GetLootRollItemLink = _G.GetLootRollItemLink
local GetLootRollItemInfo = _G.GetLootRollItemInfo

local dbDefaults = {
	profile = {
		maxHistory = 10,
		qualityThreshold = 2,
		log = {},
	},
	char = {
		lootHistory = {},
	},
}

local db
function WhoRoll:OnInitialize()
	db = LibStub("AceDB-3.0"):New("WhoRollDB", dbDefaults, "Default")
	self.db = db
	rollHistory = db.char.lootHistory
	WhoRoll.rollHistory = rollHistory
end

function WhoRoll:Log(arg)
	--[===[@alpha@
	local entry
	if type(arg) == 'table' then
		entry = CopyTable(arg)
	else
		entry = { arg }
	end
	entry.time = time()
	table.insert(db.profile.log, entry)
	--@end-alpha@]===]
end

--[===[@debug@
function WhoRoll:InsertTestLoots()
	local _, testItem = GetItemInfo(32483)
	local testLoot = {
		item = testItem,
		winner = UnitName("player"),
		choice = "GREED",
	}
	table.insert(rollHistory, testLoot)
	local _, testItem = GetItemInfo(43408)
	local testRoll = {
		item = testItem,
		rolls = {
			[UnitName("player")] = "NEED",
			test1 = "GREED",
			test2 = "DISENCHANT",
		}
	}
	currentLoots[testItem] = testRoll
	self:DebugBroker()
end
--@end-debug@]===]

function WhoRoll:OnEnable()
	self:RegisterEvent("START_LOOT_ROLL")
	self:RegisterEvent("CHAT_MSG_LOOT")
	self:UpdateTooltip()
end

local function initRoll(key)
	local value = {}
	value.rolls = {}
	value.item = key
	currentLoots[key] = value
	WhoRoll:ScheduleExpirationTimer(key)
	return value
end

local function getOrInitRoll(key)
	local value = currentLoots[key]
	if not value then
		value = initRoll(key)
	end
	return value
end

function WhoRoll:START_LOOT_ROLL(_, rollId, rollTime)
	local link = GetLootRollItemLink(rollId)
	self:Log("START_LOOT_ROLL: .. " .. rollId .. "... " .. link)
	initRoll(link)
	self:UpdateTooltip()
end

local playerName = UnitName("player")
local parsers = {
	[LOOT_ROLL_GREED_SELF] = function(message, pattern)
			return playerName, deformat(message, pattern), "GREED"
		end,
	[LOOT_ROLL_GREED] = function(message, pattern)
			local name, item = deformat(message, pattern)
			return name, item, "GREED"
		end,
	[LOOT_ROLL_NEED_SELF] = function(message, pattern)
			return playerName, deformat(message, pattern), "NEED"
		end,
	[LOOT_ROLL_NEED] = function(message, pattern)
			local name, item = deformat(message, pattern)
			return name, item, "NEED"
		end,
	[LOOT_ROLL_DISENCHANT_SELF] = function(message, pattern)
			return playerName, deformat(message, pattern), "DISENCHANT"
		end,
	[LOOT_ROLL_DISENCHANT] = function(message, pattern)
			local name, item = deformat(message, pattern)
			return name, item, "DISENCHANT"
		end,
	[LOOT_ROLL_PASSED_SELF] = function(message, pattern)
			return playerName, deformat(message, pattern), "PASS"
		end,
	[LOOT_ROLL_PASSED] = function(message, pattern)
			local name, item = deformat(message, pattern)
			return name, item, "PASS"
		end,
}

parsers[LOOT_ROLL_PASSED_SELF_AUTO] = parsers[LOOT_ROLL_PASSED_SELF]
parsers[LOOT_ROLL_PASSED_AUTO] = parsers[LOOT_ROLL_PASSED]
parsers[LOOT_ROLL_PASSED_AUTO_FEMALE] = parsers[LOOT_ROLL_PASSED]

local matchers = {}
for k in pairs(parsers) do
	matchers[k] = k:gsub("%d%$", ".+"):gsub("%%s", ".+")
end

local matchersOrder = {
	LOOT_ROLL_PASSED_SELF_AUTO,
	LOOT_ROLL_PASSED_AUTO_FEMALE,
	LOOT_ROLL_PASSED_AUTO,
	LOOT_ROLL_GREED_SELF,
	LOOT_ROLL_GREED,
	LOOT_ROLL_NEED_SELF,
	LOOT_ROLL_NEED,
	LOOT_ROLL_DISENCHANT_SELF,
	LOOT_ROLL_DISENCHANT,
	LOOT_ROLL_PASSED_SELF,
	LOOT_ROLL_PASSED_SELF,
}

local matcher_LOOT_ROLL_WON = LOOT_ROLL_WON:gsub("%d%$", ".+"):gsub("%%s", ".+")
local matcher_LOOT_ROLL_ALL_PASSED = LOOT_ROLL_ALL_PASSED:gsub("%d%$", ".+"):gsub("%%s", ".+")

local function purgeOldEntries()
	local maxEntries = db.profile.maxHistory
	while #rollHistory > maxEntries do
		table.remove(rollHistory, maxEntries + 1)
	end
end

local function setWinner(itemLink, winner)
	WhoRoll:CancelExpirationTimer(itemLink)
	local lootRoll = currentLoots[itemLink]
	if not lootRoll then return end
	lootRoll.winner = winner
	lootRoll.choice = lootRoll.rolls[winner]
	lootRoll.date = time()

	wipe(lootRoll.rolls)
	lootRoll.rolls = nil
	currentLoots[itemLink] = nil

	local _, _, quality = GetItemInfo(itemLink)
	if quality >= db.profile.qualityThreshold then
		table.insert(rollHistory, 1, lootRoll)
		purgeOldEntries()
	end
	WhoRoll:UpdateTooltip()
end

function WhoRoll:ForceFinish(itemLink)
	local itemTable = currentLoots[itemLink]
	if not itemTable then return end -- if timer is not canceled for some reason
	local rolls = currentLoots[itemLink].rolls
	for name in self:IterateGroupMembers() do
		if not rolls[name] or rolls[name] ~= "PASS" then
			setWinner(itemLink, UNKNOWN)
		end
	end
	-- that means everyone has passed
	setWinner(itemLink, NONE)
end

local function trackRoll(name, itemLink, selection)
	local logEntry = {
		tag = "trackRoll",
		name = name,
		itemLink = itemLink,
		selection = selection,
	}
	WhoRoll:Log(logEntry)
	local thisLoot = getOrInitRoll(itemLink)
	thisLoot.rolls[name] = selection
	WhoRoll:UpdateTooltip()
end

function WhoRoll:CHAT_MSG_LOOT(_, message)
	local itemLink, name, selection

	self:Log({ message = message, tag = "message", })

	if deformat(message, LOOT_ROLL_ALL_PASSED) then
		itemLink = deformat(message,LOOT_ROLL_ALL_PASSED)
		setWinner(itemLink, NONE)
		return
	end

	for _,k in ipairs(matchersOrder) do
		local v = matchers[k]
		debug("matcher: ", v)
		if message:match(v) then
			local name, itemLink, selection = parsers[k](message, k)
			trackRoll(name, itemLink, selection)
			return
		end
	end

	if deformat(message,LOOT_ROLL_WON) then
		name, itemLink = deformat(message,LOOT_ROLL_WON)
		if name == YOU then
			name = playerName
		end
		setWinner(itemLink, name)
	else
		--didn't match anything
		debug("string could not be matched")
		debug(message)
		return
	end
end

local function iteratePartyMembers()
	local i = -1
	local n = GetNumPartyMembers() + 1
	return function()
			i = i + 1
			if i == 0 then
				return (UnitName("player"))
			elseif i <= n then
				return (UnitName("party" .. i))
			end
		end
end

local function iterateRaidMembers()
	local i = 0
	local n = GetNumRaidMembers()
	return function()
			i = i + 1
			if i <= n then
				return (UnitName("raid" .. i))
			end
		end
end

function WhoRoll:IterateGroupMembers()
	return UnitInRaid("player") and iterateRaidMembers() or iteratePartyMembers()
end
